<?php

class SignWav {

	private $_directory = 'Music';
	private $_logger = null;
	private $_files = array();
	private $_excludedFileNames = null;

	public function getFileCount()
	{
		return count($this->_files);
	}

	public function __construct(Logger $logger, $directory)
	{
		$this->_logger = $logger;
		$this->_directory = $directory;

		$this->_logger->display('Folder to analyze: ' . $this->_directory);
	}

	/**
	 * Collects files, that may contain any type of audio, not necessarily one song per file
	 * @return void
	 */
	public function collectFiles()
	{
		$this->_files = array();

		$this->_logger->display("collecting files...");
		$this->_logger->event(Logger::LevelDebug, 'collectFiles', $this->_directory);

		$this->mineFolder($this->_directory);

		$this->_logger->display("files collected: {$this->getFileCount()}");
		$this->_logger->event(Logger::LevelInfo, 'collectFiles', 'count=' . count($this->_files));
	}

	public function duplicateFileNames()
	{
		return array();
	}

	public function compareHashes()
	{
		foreach ($this->_files as $file) {
			print "file:\t\t{$file->getFileName()}\n";
			print "md5:\t\t{$file->getMd5Hash()}\n";
			print "md5 audio:\t{$file->getMd5AudioOnlyHash()}\n";
		}
	}

	public function duplicateMd5Hashes()
	{
		return array();
//		foreach ($this->_files as $file) {
//			print "md5:{$file->getMd5Hash()}\n";
//		}
	}

	public function duplicateAudioOnlyMd5Hashes()
	{
		$hashToFile = array();
		$duplicates = array();
		foreach ($this->_files as $file) {
			if (array_key_exists($file->getMd5AudioOnlyHash(), $hashToFile)) {
				//@todo finish logging
				$this->_logger->event(Logger::LevelDebug, 'duplicateAudioOnlyMd5Hashes',
					"hash={$file->getMd5AudioOnlyHash()}|path1=|path2=");

				if (count($duplicates[$file->getMd5AudioOnlyHash()]) == 0) {
					// add the file this is a duplicate of
					$duplicates[$file->getMd5AudioOnlyHash()][]
						= $hashToFile[$file->getMd5AudioOnlyHash()];
				}
				$duplicates[$file->getMd5AudioOnlyHash()][] = $file;
			}
			$hashToFile[$file->getMd5AudioOnlyHash()] = $file;
		}
		return $duplicates;
	}

	public function outputToFile($fileName, $keyName, $results)
	{
		$handle = fopen($fileName, 'w');
		foreach ($results as $key => $files) {
			fwrite($handle, "{$keyName}: {$key}\n");
			foreach ($files as $file) {
				fwrite($handle, "file: {$file->getFilePath()}\n");
			}
		}
		fclose($handle);
	}

	public function reorganizeMedia($destinationFolder)
	{
		if (is_dir($destinationFolder)) {
			$this->_logger->display("The Destination '{$destinationFolder}' already exists");
			return false;
		}


		$hashToFile = array();
		$count =0;
		foreach ($this->_files as $file) {
			$tags = $file->getTags();
			$newPath = '';
			$newFileName = '';

			// get md5 audio only hash
			$audioHash = $file->getMd5AudioOnlyHash();
			$this->_logger->event(Logger::LevelInfo, 'reorganizeMedia', 'audio hash: ' . $audioHash);

			// is it a duplicate?
			$duplicate = array_key_exists($audioHash, $hashToFile);
			if ($duplicate) {
				// using the original file name, create a duplicate
				$duplicatedFile = $hashToFile[$audioHash][0];
				$newPath = $duplicatedFile->getFolder();
				$newFileName = $this->generateUniqueFileName($newPath, $duplicatedFile->getFileName(), 'dup-audio-hash');
			} else {
				// create a new unique file name
				// even though hashes might not match, ID3 might

				$fileNameTags = $tags;
				getid3_lib::CopyTagsToComments($fileNameTags);

				$artist = File::getTagValue($fileNameTags, 'artist');
				$album = File::getTagValue($fileNameTags, 'album');
				$title = File::getTagValue($fileNameTags, 'title');
				if (strlen($title) <= 0) {
					$title = File::getTagValue($fileNameTags, 'track');
				}

				$newPath = $destinationFolder
					. '/'
					. $artist
					. '/'
					. $album;

				if (!is_dir($newPath)) {
					$this->_logger->event(Logger::LevelInfo, 'reorganizeMedia', "mkdir: {$newPath}");
					mkdir($newPath, 0777, true);
				}

				$newFileName = $this->generateUniqueFileName($newPath, $title . $file->getFileExtension(), 'dup-title');
			}

			$file->cancelEdit();

			if (!$file->copyFile($newPath, $newFileName, true)) {
				$this->_logger->display("Unable to copy file!");
			}


			if (!$file->saveTags($tags)) {
				$this->_logger->display("Unable to write ID3 tags.");
			}

			$hashToFile[$file->getMd5AudioOnlyHash()][] = $file;


//			if ($count++ > 10) {
//				break;
//			}
		}

		return true;
	}
	/**********************************************************************************************
	 * PRIVATE
	 */

	private function generateUniqueFileName($newPath, $newFileName, $uniqueTag)
	{
		if (!file_exists("{$newPath}/{$newFileName}"))
		{
			return $newFileName;
		}

		for ($i = 1; $i < 10000; $i++) {
			$testName = "{$newFileName}-{$uniqueTag}-{$i}";
			if (!file_exists($newPath . '/' . $testName)) {
				$this->_logger->event(Logger::LevelInfo, 'generateUniqueFileName', "duplicate name created {$testName}");
				return $testName;
			}
		}
		return 'generationFail';
	}

	/**
	 * @param $folder
	 * @return void
	 */
	private function mineFolder($folder)
	{
		$this->_logger->event(Logger::LevelDebug, 'mineFolder', $folder);

		if ($handle = opendir($folder)) {
			while (false !== ($fileName = readdir($handle))) {
				if ($this->isExcluded($fileName)) {
					continue;
				}

				$path = "{$folder}/{$fileName}";

				if (is_dir($path)) {
					$this->mineFolder($path);
					continue;
				}

				$this->createFileEntry($folder, $fileName);
			}
			closedir($handle);
		}
	}

	private function createFileEntry($folder, $fileName)
	{
		$this->_files[] = new File($this->_logger, $folder, $fileName);
	}

	private function getExcludedFileNames()
	{
		if ($this->_excludedFileNames !== null) {
			return $this->_excludedFileNames;
		}

		$this->_excludedFileNames = array(
			'.',
			'..',
			'.DS_Store',
		);

		return $this->_excludedFileNames;
	}

	private function isExcluded($filename)
	{
		return in_array($filename, $this->getExcludedFileNames());
	}

}
